#include <stdio.h>
#include <glib.h>
#include "parser.h"
#include "csvparser.h"

typedef struct WinProcmonParser {
    CsvParser *csvparser;
    gboolean next_le_pending;
    LogElem *next_le;
} WinProcmonParser;

static int
winprocmon_parser_create (WinProcmonParser **parser, FILE *f) {
    (*parser) = g_new0 (WinProcmonParser, 1);
    if (*parser == NULL)
        goto error;
    (*parser)->csvparser = CsvParser_new_from_handler (f, ",", 1);
    const CsvRow *header = CsvParser_getHeader((*parser)->csvparser);
    if (header == NULL) {
        printf("%s\n", CsvParser_getErrorMessage((*parser)->csvparser));
        return -1;
    }
    (*parser)->next_le_pending = FALSE;
    LogElem *le;
    if (creat_log_elem (&le) != 0) {
        goto error;
    }
    (*parser)->next_le = le;
    return 0;
    error:
    return -1;
}

static int
winprocmon_parser_parse (void *opaque, FILE *f, LogElem *le) {
    #define WINPROCMON_ROW_NUM 7
    WinProcmonParser *parser = (WinProcmonParser *) opaque;
    CsvRow *row;
    if (parser->next_le_pending) {
        log_elem_populate (le, parser->next_le->type, parser->next_le->src_type, parser->next_le->src,
                                                      parser->next_le->dest_type, parser->next_le->dest);
        clean_log_elem (parser->next_le);
        parser->next_le_pending = FALSE;
        goto incoming;
    }
    for (;;) {
        row  = CsvParser_getRow (parser->csvparser);
        if (row == NULL) break;
        const char ** rowFields = CsvParser_getFields (row);
        if (CsvParser_getNumFields (row) != WINPROCMON_ROW_NUM) {
            CsvParser_destroy_row(row);
            return PARSE_LOG_ERR;
        }
        /* fork exec */
        if (!strcmp(rowFields[3], "Process Create")){
            GRegex *rx;
            GMatchInfo *match_info;
            gchar *str;
            rx = g_regex_new ("(?<=(PID: ))[0-9]*", G_REGEX_DEFAULT,  G_REGEX_MATCH_DEFAULT, NULL);
            if (g_regex_match (rx, rowFields[6], 0, &match_info)) {
                str = g_match_info_fetch (match_info, 0);
                log_elem_populate (le, LE_FORK, LEP_PID,rowFields[2], LEP_PID, str);
                log_elem_populate (parser->next_le, LE_EXEC, LEP_FILE, rowFields[4], LEP_PID,  str);
                parser->next_le_pending = TRUE;
                g_free (str);
                g_match_info_free (match_info);
            }
            g_regex_unref (rx);
            CsvParser_destroy_row(row);
            goto incoming;
        }
        
        if (!strcmp(rowFields[3], "IRP_MJ_READ")) {
            if (strstr (rowFields[4], ".dll") != NULL) continue;
            log_elem_populate (le, LE_READ, LEP_FILE, rowFields[4], LEP_PID, rowFields[2]);
            CsvParser_destroy_row(row);
            goto incoming;
        }

        if (!strcmp(rowFields[3], "IRP_MJ_WRITE")) {
            if (strstr (rowFields[4], ".dll") != NULL) continue;
            log_elem_populate (le, LE_WRITE, LEP_PID, rowFields[2], LEP_FILE, rowFields[4]);
            CsvParser_destroy_row(row);
            goto incoming;
        }

        if (!strcmp (rowFields[3], "TCP Send")) {
            GRegex *rx;
            GMatchInfo *match_info;
            gchar *src_port, *dest_port;
            rx = g_regex_new ("(.*)(?=( -> ))", G_REGEX_DEFAULT,  G_REGEX_MATCH_DEFAULT, NULL);
            if (g_regex_match (rx, rowFields[4], 0, &match_info)) {
                src_port = g_match_info_fetch (match_info, 0);
                g_match_info_free (match_info);
            }
            g_regex_unref (rx);
            rx = g_regex_new ("(?<=( -> ))(.*)", G_REGEX_DEFAULT,  G_REGEX_MATCH_DEFAULT, NULL);
            if (g_regex_match (rx, rowFields[4], 0, &match_info)) {
                dest_port = g_match_info_fetch (match_info, 0);
                g_match_info_free (match_info);
            }
            g_regex_unref (rx);
            log_elem_populate (le, LE_REFER, LEP_RES, src_port, LEP_PID,  rowFields[2]);            
            log_elem_populate (parser->next_le, LE_SEND, LEP_PORT, src_port, LEP_PORT, dest_port);
            parser->next_le_pending = TRUE;
            g_free (src_port);
            g_free (dest_port);
            CsvParser_destroy_row(row);
            goto incoming;
        }

        if (!strcmp (rowFields[3], "UDP Send")) {
            GRegex *rx;
            GMatchInfo *match_info;
            gchar *src_port, *dest_port;
            rx = g_regex_new ("(.*)(?=( -> ))", G_REGEX_DEFAULT,  G_REGEX_MATCH_DEFAULT, NULL);
            if (g_regex_match (rx, rowFields[4], 0, &match_info)) {
                src_port = g_match_info_fetch (match_info, 0);
                g_match_info_free (match_info);
            }
            g_regex_unref (rx);
            rx = g_regex_new ("(?<=( -> ))(.*)", G_REGEX_DEFAULT,  G_REGEX_MATCH_DEFAULT, NULL);
            if (g_regex_match (rx, rowFields[4], 0, &match_info)) {
                dest_port = g_match_info_fetch (match_info, 0);
                g_match_info_free (match_info);
            }
            g_regex_unref (rx);
            log_elem_populate (le, LE_REFER, LEP_RES, src_port, LEP_PID,  rowFields[2]);            
            log_elem_populate (parser->next_le, LE_SEND, LEP_PORT, src_port, LEP_PORT, dest_port);
            parser->next_le_pending = TRUE;
            g_free (src_port);
            g_free (dest_port);
            CsvParser_destroy_row(row);
            goto incoming;
        }

         if (!strcmp (rowFields[3], "TCP Receive")) {
            GRegex *rx;
            GMatchInfo *match_info;
            gchar *src_port, *dest_port;
            rx = g_regex_new ("(.*)(?=( -> ))", G_REGEX_DEFAULT,  G_REGEX_MATCH_DEFAULT, NULL);
            if (g_regex_match (rx, rowFields[4], 0, &match_info)) {
                src_port = g_match_info_fetch (match_info, 0);
                g_match_info_free (match_info);
            }
            g_regex_unref (rx);
            rx = g_regex_new ("(?<=( -> ))(.*)", G_REGEX_DEFAULT,  G_REGEX_MATCH_DEFAULT, NULL);
            if (g_regex_match (rx, rowFields[4], 0, &match_info)) {
                dest_port = g_match_info_fetch (match_info, 0);
                g_match_info_free (match_info);
            }
            g_regex_unref (rx);
            log_elem_populate (le, LE_REFER, LEP_RES, src_port, LEP_PID,  rowFields[2]);
            log_elem_populate (parser->next_le, LE_RECV, LEP_PORT, dest_port, LEP_PORT, src_port);            
            parser->next_le_pending = TRUE;
            g_free (src_port);
            g_free (dest_port);
            CsvParser_destroy_row(row);
            goto incoming;
        }

        if (!strcmp (rowFields[3], "UDP Receive")) {
            GRegex *rx;
            GMatchInfo *match_info;
            gchar *src_port, *dest_port;
            rx = g_regex_new ("(.*)(?=( -> ))", G_REGEX_DEFAULT,  G_REGEX_MATCH_DEFAULT, NULL);
            if (g_regex_match (rx, rowFields[4], 0, &match_info)) {
                src_port = g_match_info_fetch (match_info, 0);
                g_match_info_free (match_info);
            }
            g_regex_unref (rx);
            rx = g_regex_new ("(?<=( -> ))(.*)", G_REGEX_DEFAULT,  G_REGEX_MATCH_DEFAULT, NULL);
            if (g_regex_match (rx, rowFields[4], 0, &match_info)) {
                dest_port = g_match_info_fetch (match_info, 0);
                g_match_info_free (match_info);
            }
            g_regex_unref (rx);
            log_elem_populate (le, LE_REFER, LEP_RES, src_port, LEP_PID,  rowFields[2]);
            log_elem_populate (parser->next_le, LE_RECV, LEP_PORT, dest_port, LEP_PORT, src_port);            
            parser->next_le_pending = TRUE;
            g_free (src_port);
            g_free (dest_port);
            CsvParser_destroy_row(row);
            goto incoming;
        }

        CsvParser_destroy_row(row);
    }
    return PARSE_LOG_END;
    incoming:
    return PARSE_LOG_INCOMING;
}

static int
winprocmon_parser_destruct (void *opaque) {

}

int
winprocmon_log_parser_create (LogParser **p, FILE *f) {
    WinProcmonParser *parser;
    int stat;
    if (p == NULL) goto error;
    *p  = g_new0 (LogParser, 1);
    if (*p == NULL) goto error;
    stat = winprocmon_parser_create (&parser, f);

    (*p)->opaque = parser;
    (*p)->log_file = f;
    (*p)->parse = winprocmon_parser_parse;
    (*p)->destruct = winprocmon_parser_destruct;

    return 0;
    error:
    return -1;
}